package cs340.stormtroopers.hypeerweb;

import java.util.ArrayList;

/**
 * Represents the connections a given node has to other nodes. </br>
 * 
 * <pre>
 * <b>Instance Domain:</b>
 * 		neighborsBiggerThanMe:               ArrayList<Node>
 * 		childlessNeighborsSmallerThanMe:     ArrayList<Node>
 * 		parentNeighborsSmallerThanMe:        ArrayList<Node>
 * 		upPointers:                          ArrayList<Node>
 * 		downPointers:                        ArrayList<Node>
 * 		fold:                                Node
 * 		surrogateFold:                       Node
 * 		inverseSurrogateFold:                Node
 * 
 * <b>Instance Variants</b>
 * 		neighborsBiggerThanMe is a non-null list of nodes
 * 			-This list of nodes has webId's greater than the webId of this node.
 * 		childlessNeighborsSmallerThanMe is a non-null list of nodes
 * 			-This list of nodes has webId's smaller than the webId of this node.
 * 			-All these nodes have not had any children added to them.
 * 		parentNeighborsSmallerThanMe is a non-null list of nodes
 * 			-This list of nodes has webId's smaller than the webId of this node.
 * 			-All these nodes have had children added to them.
 * 		upPointers is a non-null list of nodes
 * 			-This lists all the nodes with neighboring webId's to the child webId of this node
 * 				when this node has no child.
 * 			-This list only contains nodes if this node has no child.
 * 		downPointers is a non-null list of nodes
 * 			-This is a list of all the parents of webId's neighboring this node for which no
 * 				node has been created.
 * 			-This list only contains nodes if this node is an edge node.
 * 		fold is the node whose webId is the binary complement of this node's webId.
 * 		surrogateFold is the parent of the webId that is the binary complement of this
 * 			node's webId if the webId of the fold has had no node created for it.
 * 		inverseSurrogateFold is the fold of child webId of this node's webId.
 * </pre>
 * 
 * 
 * @author Andy Morrow
 * @author Brad Spendlove
 * @author Daniel Howard
 * @author James Gurr
 * @author William Highland
 * 
 */

public class Connections
{
	private ArrayList<Node> neighborsBiggerThanMe;
	private ArrayList<Node> childlessNeighborsSmallerThanMe;
	private ArrayList<Node> parentNeighborsSmallerThanMe;
	private ArrayList<Node> upPointers;
	private ArrayList<Node> downPointers;
	private Node fold;
	private Node surrogateFold;
	private Node inverseSurrogateFold;

	public Connections()
	{
	}

	/**
	 * Public constructor creates the connections calculated for this node in
	 * the node's constructor.
	 * 
	 * @param neighborsBiggerThanMe
	 *            list of nodes whose webId's are bigger than this node's webId.
	 * @param childlessNeighborsSmallerThanMe
	 *            list of nodes without children whose webId's are smaller than
	 *            this node's webId.
	 * @param parentNeighborsSmallerThanMe
	 *            list of nodes with children whose webId's are smaller than
	 *            this node's webId.
	 * @param upPointers
	 *            list of all neighbors of this node's child's webId.
	 * @param downPointers
	 *            list of all parents of neighboring webId's for which a node
	 *            has not yet been created.
	 * @param fold
	 *            binary complement of this node's webId.
	 * @param surrogateFold
	 *            the parent of this webId's fold if the webId for the fold has
	 *            no node.
	 * @param inverseSurrogateFold
	 *            the fold of this webId's child webId if no node has been
	 *            created for it yet.
	 * 
	 * @pre <pre>
	 * 		neighborsBiggerThanMe is not null AND</br>
	 * 		childlessNeighborsSmallerThanMe is not null AND</br>
	 * 		parentNeighborsSmallerThanMe is not null AND</br>
	 * 		upPointers is not null AND</br>
	 * 		downPointers is not null</br>
	 * </pre>
	 * 
	 * @post <pre>
	 * 		neighborsBiggerThanMe = new list of neighbors bigger than this node AND</br>
	 * 		childlessNeighborsSmallerThanMe = new list of childless nodes smaller than this node AND</br>
	 * 		parentNeighborsSmallerThanMe = new list of parent nodes smaller than this node AND</br>
	 * 		upPointers = new list of nodes AND</br>
	 * 		downPointers = new list of nodes AND</br>
	 * 		fold = the fold calculated for this node AND</br>
	 * 		surrogateFold = the surrogateFold calculated for this node AND</br>
	 * 		inverseSurrogateFold = the inverseSurrogateFold calculated for this node</br>
	 * 
	 * </pre>
	 */
	public Connections(ArrayList<Node> neighborsBiggerThanMe,
			ArrayList<Node> childlessNeighborsSmallerThanMe,
			ArrayList<Node> parentNeighborsSmallerThanMe,
			ArrayList<Node> upPointers, ArrayList<Node> downPointers,
			Node fold, Node surrogateFold, Node inverseSurrogateFold)
	{
		// These MUST be copy constructors because the original lists are
		// subject to change.
		// DON'T CHANGE THIS TO SIMPLE ASSIGNMENT!
		this.neighborsBiggerThanMe = new ArrayList<Node>(neighborsBiggerThanMe);
		this.childlessNeighborsSmallerThanMe = new ArrayList<Node>(
				childlessNeighborsSmallerThanMe);
		this.parentNeighborsSmallerThanMe = new ArrayList<Node>(
				parentNeighborsSmallerThanMe);
		this.upPointers = new ArrayList<Node>(upPointers);
		this.downPointers = new ArrayList<Node>(downPointers);
		this.fold = fold;
		this.surrogateFold = surrogateFold;
		this.inverseSurrogateFold = inverseSurrogateFold;
	}

	/**
	 * Copy constructor for Connections.
	 * 
	 * @param other
	 *            Connections object being copied
	 * 
	 * @pre other is not null
	 */
	/*
	 * public Connections(Connections other) { neighborsBiggerThanMe = new
	 * ArrayList<Node>(other.getNeighborsBiggerThanMe());
	 * childlessNeighborsSmallerThanMe = new
	 * ArrayList<Node>(other.getChildlessNeighborsSmallerThanMe());
	 * parentNeighborsSmallerThanMe = new
	 * ArrayList<Node>(other.getParentNeighborsSmallerThanMe()); upPointers =
	 * new ArrayList<Node>(other.getUpPointers()); downPointers = new
	 * ArrayList<Node>(other.getDownPointers()); fold = new
	 * Node(other.getFold()); surrogateFold = new
	 * Node(other.getSurrogateFold()); inverseSurrogateFold = new
	 * Node(other.getInverseSurrogateFold()); }
	 */

	/**
	 * Returns the <i>inverseSurrogateFold</i>. Also known as the
	 * <i>inverseSurrogateFold</i> getter.
	 * 
	 * @return inverseSurrogateFold
	 * 
	 * @pre <i>None</i>
	 * @post result = inverseSurrogateFold
	 */
	public Node getInverseSurrogateFold()
	{
		return inverseSurrogateFold;
	}

	/**
	 * Returns the <i>surrogateFold</i>. Also known as the <i>surrogateFold</i>
	 * getter.
	 * 
	 * @return surrogateFold
	 * 
	 * @pre <i>None</i>
	 * @post result = surrogateFold
	 */
	public Node getSurrogateFold()
	{
		return surrogateFold;
	}

	/**
	 * Returns the <i>fold</i>. Also known as the <i>fold</i> getter.
	 * 
	 * @return fold
	 * 
	 * @pre <i>None</i>
	 * @post result = fold
	 */
	public Node getFold()
	{
		return fold;
	}

	/**
	 * Creates a list of all neighbors from <i>neighborsBiggerThanMe</i>,
	 * <i>childlessNeighborsSmallerThanMe</i>, and
	 * <i>parentNeighborsSmallerThanMe</i>, and returns this list.
	 * 
	 * @return neighbors Returns a list of neighbors created from the 3 lists of
	 *         neighboring connections
	 * 
	 * @pre <i>None</i>
	 * @post result = neighbors
	 */
	public ArrayList<Node> getNeighbors()
	{
		ArrayList<Node> neighbors = new ArrayList<Node>();
		neighbors.addAll(parentNeighborsSmallerThanMe);
		neighbors.addAll(childlessNeighborsSmallerThanMe);
		neighbors.addAll(neighborsBiggerThanMe);

		return neighbors;
	}

	/**
	 * Return the <i>upPointers</i>. Also known as the <i>upPointers</i> getter.
	 * 
	 * @return upPointers
	 * 
	 * @pre <i>None</i>
	 * @post result = upPointers
	 */
	public ArrayList<Node> getUpPointers()
	{
		return upPointers;
	}

	/**
	 * Returns the <i>downPointers</i>. Also known as the <i>downPointers</i>
	 * getter.
	 * 
	 * @return downPointers
	 * 
	 * @pre <i>None</i>
	 * @post result = downPointers
	 */
	public ArrayList<Node> getDownPointers()
	{
		return downPointers;
	}

	/**
	 * Returns the <i>neighborsBiggerThanMe</i>. Also known as the
	 * <i>neighborsBiggerThanMe</i> getter.
	 * 
	 * @return neighborsBiggerThanMe
	 * 
	 * @pre <i>None</i>
	 * @post result = neighborsBiggerThanMe
	 */
	public ArrayList<Node> getNeighborsBiggerThanMe()
	{
		return neighborsBiggerThanMe;
	}

	/**
	 * Returns the <i>parentNeighborsSmallerThanMe</i>. Also known as the
	 * <i>parentNeighborsSmallerThanMe</i> getter.
	 * 
	 * @return parentNeighborsSmallerThanMe
	 * 
	 * @pre <i>None</i>
	 * @post result = parentNeighborsSmallerThanMe
	 */
	public ArrayList<Node> getParentNeighborsSmallerThanMe()
	{
		return parentNeighborsSmallerThanMe;
	}

	/**
	 * Returns the <i>childlessNeighborsSmallerThanMe</i> Also known as the
	 * <i>childlessNeighborsSmallerThanMe</i> getter.
	 * 
	 * @return childlessNeighborsSmallerThanMe
	 * 
	 * @pre <i>None</i>
	 * @post result = childlessNeighborsSmallerthanMe
	 */
	public ArrayList<Node> getChildlessNeighborsSmallerThanMe()
	{
		return childlessNeighborsSmallerThanMe;
	}

	/**
	 * Sets the neighborsBiggerThanMe of this Connections to the ArrayList<Node>
	 * representing the neighborsBiggerThanMe.
	 * 
	 * @param neighborsBiggerThanMe
	 *            List of all neighbors whose webId's are bigger than this
	 *            node's webId
	 * 
	 * @pre neighborsBiggerThanMe &ne; null
	 * @post this.neighborsBiggerThanMe = neighborsBiggerThanMe
	 */
	public void setNeighborsBiggerThanMe(ArrayList<Node> neighborsBiggerThanMe)
	{
		this.neighborsBiggerThanMe = neighborsBiggerThanMe;
	}

	/**
	 * Sets the childlessNeighborsSmallerThanMe of this Connections to the
	 * ArrayList<Node> representing the childlessNeighborsSmallerThanMe.
	 * 
	 * @param childlessNeighborsSmallerThanMe
	 * 
	 * @pre childlessNeighborsSmallerThanMe &ne; null
	 * @post this.childlessNeighborsSmallerThanMe =
	 *       childlessNeighborsSmallerThanMe
	 */
	public void setChildlessNeighborsSmallerThanMe(
			ArrayList<Node> childlessNeighborsSmallerThanMe)
	{
		this.childlessNeighborsSmallerThanMe = childlessNeighborsSmallerThanMe;
	}

	/**
	 * Sets the parentNeighborsSmallerThanMe of this Connections to the
	 * ArrayList<Node> representing the parentNeighborsSmallerThanMe.
	 * 
	 * @param parentNeighborsSmallerThanMe
	 * 
	 * @pre parentNeighborsSmallerThenMe &ne; null
	 * @post this.parentNeighborsSmallerThanMe = parentNeighborsSmallerThanMe
	 */
	public void setParentNeighborsSmallerThanMe(
			ArrayList<Node> parentNeighborsSmallerThanMe)
	{
		this.parentNeighborsSmallerThanMe = parentNeighborsSmallerThanMe;
	}

	/**
	 * Sets the upPointers of this Connections to the ArrayList<Node>
	 * representing the upPointers.
	 * 
	 * @param upPointers
	 * 
	 * @pre upPointers &ne; null
	 * @post this.upPointers = upPointers
	 */
	public void setUpPointers(ArrayList<Node> upPointers)
	{
		this.upPointers = upPointers;
	}

	/**
	 * Sets the downPointers of this Connections to the ArrayList<Node>
	 * representing the downPointers.
	 * 
	 * @param downPointers
	 * 
	 * @pre downPointers &ne; null
	 * @post this.downPointers = downPointers
	 */
	public void setDownPointers(ArrayList<Node> downPointers)
	{
		this.downPointers = downPointers;
	}

	/**
	 * Sets the fold of this Connections to Node representing the fold
	 * 
	 * @param fold
	 * 
	 * @pre <i>None</i>
	 * @post this.fold = fold
	 */
	public void setFold(Node fold)
	{
		this.fold = fold;
	}

	/**
	 * Sets the surrogateFold of this Connections to Node representing the
	 * surrogateFold
	 * 
	 * @param surrogateFold
	 * 
	 * @pre <i>None</i>
	 * @post this.surrogateFold = surrogateFold
	 */
	public void setSurrogateFold(Node surrogateFold)
	{
		this.surrogateFold = surrogateFold;
	}

	/**
	 * Sets the inverseSurrogateFold of this Connections to Node representing
	 * the inverseSurrogateFold
	 * 
	 * @param inverseSurrogateFold
	 * 
	 * @pre <i>None</i>
	 * @post this.inverseSurrogateFold = inverseSurrogateFold
	 */
	public void setInverseSurrogateFold(Node inverseSurrogateFold)
	{
		this.inverseSurrogateFold = inverseSurrogateFold;
	}

	/**
	 * Adds a neighbor to the neighboring Node to this Connections. The
	 * neighbors are represented as three ArrayLists that keep the neighbors
	 * sorted which makes optimizes searching and inserting. The neighbor is
	 * added to the appropriate list
	 * 
	 * @param neighbor
	 *            the node we are going to add as a new neighbor
	 * 
	 * @pre neighbor &ne; null
	 * @post result = neighborsBiggerThanMe + neighbor such that neighbor is
	 *       larger than all nodes in neighborsBiggerThanMe and is added to the
	 *       end of the list (this list is sorted in ascending order).
	 */
	public void addNeighbor(Node neighbor)
	{
		neighborsBiggerThanMe.add(neighbor);
	}

	/**
	 * Adds a new downPointer
	 * 
	 * @param downPointer
	 *            the downPointer to be added to the list
	 * 
	 * @pre downPointer &ne; null
	 * @post result = downPointers + downPointer
	 */
	public void addDownPointer(Node downPointer)
	{
		downPointers.add(downPointer);
	}

	/**
	 * Removes the specified downPointer from the list of downPointers
	 * 
	 * @param downPointer
	 *            the downPointer to be removed from the list
	 * 
	 * @pre downPointer &ne; null
	 * @post result = downPointers - downPointer if downPointer is not found in
	 *       downPointers, no changes occur
	 * 
	 */
	public void removeDownPointer(Node downPointer)
	{
		downPointers.remove(downPointer);
	}

	/**
	 * Adds a new upPointer
	 * 
	 * @param upPointer
	 *            the upPointer to be added
	 * 
	 * @pre upPointer &ne; null
	 * @post result = upPointers - upPointer if upPointer is not found in
	 *       upPointers, no changes occur
	 */
	public void addUpPointer(Node upPointer)
	{
		upPointers.add(upPointer);
	}

	/**
	 * Removes a neighboring node from the appropriate list of neighboring
	 * nodes. All neighboring nodes are contained in three lists that are
	 * separate based on how they are related to the node containing this
	 * Connections.
	 * 
	 * @param neighbor
	 *            the neighboring node to be removed
	 * 
	 * @pre neighbor &ne; null
	 * @post result = neighborsBiggerThanMe - neighbor if neighbor is not found
	 *       in neighborsBiggerThanMe, then no changes occur
	 */
	public void removeNeighbor(Node neighbor)
	{
		neighborsBiggerThanMe.remove(neighbor);
	}

	/**
	 * Removes the upPointer from the upPointers
	 * 
	 * @param upPointer
	 *            the upPointer to be removed
	 * 
	 * @pre upPointer &ne; null
	 * @post result = upPointers - upPointer if upPointer is not found in
	 *       upPointers, then no changes occur
	 */
	public void removeUpPointer(Node upPointer)
	{
		upPointers.remove(upPointer);
	}

	/**
	 * Completely empties the list of upPointers for this node. This occurs when
	 * a node has a child.
	 * 
	 * @pre <i>None</i>
	 * @post upPointers is completely empty
	 */
	public void clearUpPointers()
	{
		upPointers.clear();
	}

	/**
	 * Removes all connections for this object For use in clearing node for
	 * deletion
	 * 
	 * @pre <i>None</i>
	 * @post All data structures should be empty and folds should be null
	 */
	public void clearAll()
	{
		neighborsBiggerThanMe.clear();
		childlessNeighborsSmallerThanMe.clear();
		parentNeighborsSmallerThanMe.clear();
		upPointers.clear();
		downPointers.clear();
		fold = null;
		surrogateFold = null;
		inverseSurrogateFold = null;
	}

	/**
	 * Changes a node from the list of <i>childlessNeighborsSmallerThanMe</i> to
	 * the list of <i>parentNeighborsBiggerThanMe</i> if the node has a child
	 * node.
	 * 
	 * @param neighbor
	 *            the neighbor that had the child
	 * 
	 * @pre neighbor &ne; null AND neighbor is in the list
	 *      childlessNeighborsSmallerThanMe
	 * @post neighbor is removed from childlessNeighborsSmallerThanMe AND
	 *       neighbor is added to parentNeighborsSmallerThanMe
	 * 
	 */
	public void neighborHadChild(Node neighbor)
	{
		childlessNeighborsSmallerThanMe.remove(neighbor);
		parentNeighborsSmallerThanMe.add(neighbor);
	}

	/**
	 * Replace an old neighbor with a new one in the appropriate neighbor list.
	 * 
	 * @param oldNeighbor
	 *            the neighbor being replaced
	 * @param newNeighbor
	 *            the new neighbor taking the old neighbor's place
	 * 
	 * @pre oldNeighbor &ne; null AND newNeighbor &ne; null
	 * @post oldNeighbor is replaced with newNeighbor in neighborsBiggerThanMe
	 *       OR</br> oldNeighbor is replaced with newNeighbor in
	 *       childlessNeighborsSmallerThanMe OR</br> oldNeighbor is replaced
	 *       with newNeighbor in parentNeighborsSmallerThanMe</br>
	 */
	public void replaceNeighbor(Node oldNeighbor, Node newNeighbor)
	{
		// we use the indices to keep the lists in order
		int index = neighborsBiggerThanMe.indexOf(oldNeighbor);
		if (index >= 0)
			neighborsBiggerThanMe.set(index, newNeighbor);

		index = childlessNeighborsSmallerThanMe.indexOf(oldNeighbor);
		if (index >= 0)
			childlessNeighborsSmallerThanMe.set(index, newNeighbor);

		index = parentNeighborsSmallerThanMe.indexOf(oldNeighbor);
		if (index >= 0)
			parentNeighborsSmallerThanMe.set(index, newNeighbor);
	}

	/**
	 * Replaces a downPointer to an old node with a downPointer to a new node
	 * 
	 * @param oldNode
	 *            the downPointer node being replaced
	 * @param newNode
	 *            the new node the new downPointer points to
	 * 
	 * @pre oldNode &ne; null AND newNode &ne; null
	 * @post oldNeighbor is replaced with newNode in downPointers
	 */
	public void replaceDownPointer(Node oldNode, Node newNode)
	{
		// keep lists in order
		int index = downPointers.indexOf(oldNode);
		if (index >= 0)
			downPointers.set(index, newNode);
	}

	/**
	 * Replaces an upPointer to an old node with an upPointer to a new node
	 * 
	 * @param oldNode
	 *            the old upPointer to the old node
	 * @param newNode
	 *            the new upPOinter to the new node
	 * 
	 * @pre oldNode &ne; null AND newNode &ne; null
	 * @post oldNode is replaced with newNode in upPointers
	 */
	public void replaceUpPointer(Node oldNode, Node newNode)
	{
		// keep lists in order
		int index = upPointers.indexOf(oldNode);
		if (index >= 0)
			upPointers.set(index, newNode);
	}
}
